#!/bin/bash
#
# Linux系统健康监控与邮件告警脚本
# 功能：监控系统关键性能指标，超过阈值时通过QQ邮箱发送告警邮件
# 适用系统：CentOS/Red Hat
#

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # 恢复默认颜色

# 日志文件
LOG_FILE="/var/log/system_monitor.log"
TEMP_FILE="/tmp/health_report.txt"

# 邮件配置
EMAIL_TO=""                               # 告警邮件接收者
EMAIL_FROM=""                             # 发件人邮箱
EMAIL_PASS=""                             # SMTP服务器地址
SMTP_SERVER="smtp.qq.com"
SMTP_PORT="587"

# 阈值设置（可根据实际情况调整）
CPU_THRESHOLD=80      # CPU使用率阈值（百分比）
MEM_THRESHOLD=80      # 内存使用率阈值（百分比）
DISK_THRESHOLD=80     # 磁盘使用率阈值（百分比）
LOAD_THRESHOLD=$(nproc) # 系统负载阈值（默认为CPU核心数）

# 检查依赖工具是否安装
check_dependencies() {
    echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] 检查依赖工具...${NC}"
    
    # 检查必要的命令是否存在
    for cmd in mailx bc awk grep sed; do
        if ! command -v $cmd &> /dev/null; then
            echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] 未找到 $cmd 命令，尝试安装...${NC}"
            
            # 安装缺失的依赖
            if [[ -f /etc/redhat-release ]]; then
                # CentOS/RHEL系统
                if [[ $cmd == "mailx" ]]; then
                    yum install -y mailx
                else
                    yum install -y $cmd
                fi
            elif [[ -f /etc/debian_version ]]; then
                # Debian/Ubuntu系统
                if [[ $cmd == "mailx" ]]; then
                    apt-get update && apt-get install -y mailutils
                else
                    apt-get update && apt-get install -y $cmd
                fi
            else
                echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] 不支持的Linux发行版，请手动安装 $cmd${NC}"
                exit 1
            fi
            
            # 检查安装是否成功
            if ! command -v $cmd &> /dev/null; then
                echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] 安装 $cmd 失败，请手动安装后再运行脚本${NC}"
                exit 1
            fi
        fi
    done
    
    echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] 所有依赖已满足${NC}"
}

# 配置邮件发送
configure_mail() {
    echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] 配置邮件发送...${NC}"
    
    # 检查mailx配置文件是否存在
    if [[ ! -f /etc/mail.rc ]]; then
        echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] 未找到mailx配置文件，创建新配置...${NC}"
        touch /etc/mail.rc
    fi
    
    # 配置mailx
    cat > /etc/mail.rc << EOF
set smtp-use-starttls
set ssl-verify=ignore
set nss-config-dir=/etc/pki/nssdb
set smtp-auth-password=${EMAIL_PASS}
set smtp-auth-user=${EMAIL_FROM}
set smtp-auth=login
set smtp=smtp://${SMTP_SERVER}:${SMTP_PORT}
set from=${EMAIL_FROM}
set ssl-verify=ignore
set nss-config-dir=/etc/pki/nssdb
# 完全禁用SSL证书验证
set ssl-verify=ignore
EOF

    # 创建证书目录（如果不存在）
    if [ ! -d /etc/pki/nssdb ]; then
        mkdir -p /etc/pki/nssdb
        chmod 755 /etc/pki/nssdb
    fi
    
    echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] 邮件配置完成${NC}"
}

# 获取CPU使用率
get_cpu_usage() {
    # 方法1：获取总体CPU使用率
    CPU_IDLE=$(top -bn1 | grep "%Cpu" | awk '{print $8}')
    
    # 确保获取到的是有效数字
    if [[ -n "$CPU_IDLE" && "$CPU_IDLE" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
        # 计算总体CPU使用率 = 100 - 空闲率
        TOTAL_CPU_USAGE=$(echo "100 - $CPU_IDLE" | bc)
        TOTAL_CPU_USAGE=${TOTAL_CPU_USAGE%.*}
        
        # 确保CPU使用率不为负数
        if [[ "$TOTAL_CPU_USAGE" -lt 0 ]]; then
            TOTAL_CPU_USAGE=0
        fi
    else
        TOTAL_CPU_USAGE=0
    fi
    
    # 方法2：获取单个进程的最高CPU使用率
    MAX_PROCESS_CPU=$(top -bn1 | grep -v "%Cpu" | awk 'NR>7 {print $9}' | head -1)
    
    # 确保获取到的是有效数字
    if [[ -n "$MAX_PROCESS_CPU" && "$MAX_PROCESS_CPU" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
        MAX_PROCESS_CPU=${MAX_PROCESS_CPU%.*}
    else
        MAX_PROCESS_CPU=0
    fi
    
    # 取两者中的较大值作为最终的CPU使用率
    if [[ $MAX_PROCESS_CPU -gt $TOTAL_CPU_USAGE ]]; then
        CPU_USAGE=$MAX_PROCESS_CPU
    else
        CPU_USAGE=$TOTAL_CPU_USAGE
    fi
    
    # 添加调试输出
    echo "DEBUG: CPU空闲率=$CPU_IDLE%, 总体CPU使用率=$TOTAL_CPU_USAGE%, 最高进程CPU=$MAX_PROCESS_CPU%, 最终CPU使用率=$CPU_USAGE%" >> $LOG_FILE
    
    echo $CPU_USAGE
}

# 获取内存使用率
get_mem_usage() {
    MEM_USAGE=$(free | grep Mem | awk '{print int($3/$2 * 100)}')
    echo $MEM_USAGE
}

# 获取系统负载
get_system_load() {
    SYSTEM_LOAD=$(uptime | awk -F'[a-z]:' '{print $2}' | awk -F',' '{print $1}' | tr -d ' ')
    echo $SYSTEM_LOAD
}

# 获取磁盘使用情况
get_disk_usage() {
    DISK_USAGE=$(df -h | grep -vE '^Filesystem|tmpfs|cdrom|none' | awk '{ print $5 " " $6 }' | sed 's/%//g')
    echo "$DISK_USAGE"
}

# 检查系统进程
check_processes() {
    echo -e "\n==== 系统进程状态 ====\n" >> $TEMP_FILE
    echo -e "以下是CPU和内存占用最高的10个进程：\n" >> $TEMP_FILE
    ps aux --sort=-%cpu,%mem | head -11 >> $TEMP_FILE
    echo -e "\n" >> $TEMP_FILE
}

# 检查系统服务
check_services() {
    echo -e "\n==== 关键系统服务状态 ====\n" >> $TEMP_FILE
    
    # 检查常见的重要服务
    for service in sshd crond firewalld network; do
        if systemctl is-active $service &>/dev/null; then
            echo -e "$service: 运行中" >> $TEMP_FILE
        else
            echo -e "$service: 未运行" >> $TEMP_FILE
        fi
    done
    echo -e "\n" >> $TEMP_FILE
}

# 检查网络连接
check_network() {
    echo -e "\n==== 网络连接状态 ====\n" >> $TEMP_FILE
    
    # 检查是否有netstat命令
    if command -v netstat &> /dev/null; then
        echo -e "活动的网络连接数: $(netstat -an | grep ESTABLISHED | wc -l)" >> $TEMP_FILE
        echo -e "监听端口:" >> $TEMP_FILE
        netstat -tuln | grep LISTEN >> $TEMP_FILE
    # 尝试使用ss命令替代
    elif command -v ss &> /dev/null; then
        echo -e "活动的网络连接数: $(ss -an | grep ESTAB | wc -l)" >> $TEMP_FILE
        echo -e "监听端口:" >> $TEMP_FILE
        ss -tuln | grep LISTEN >> $TEMP_FILE
    else
        echo -e "无法获取网络连接信息: netstat和ss命令均不可用" >> $TEMP_FILE
        echo -e "请安装net-tools或iproute2软件包" >> $TEMP_FILE
    fi
    
    echo -e "\n" >> $TEMP_FILE
}

# 检查最近的登录尝试
check_logins() {
    echo -e "\n==== 最近的登录尝试 ====\n" >> $TEMP_FILE
    last | head -10 >> $TEMP_FILE
    echo -e "\n" >> $TEMP_FILE
}

# 生成健康报告
generate_health_report() {
    local alert_level="正常"
    local alert_color="green"
    local alert_items=""
    
    # 清空临时文件
    > $TEMP_FILE
    
    # 获取主机信息
    HOSTNAME=$(hostname)
    IP_ADDRESS=$(hostname -I | awk '{print $1}')
    KERNEL=$(uname -r)
    UPTIME=$(uptime -p)
    
    # 获取各项指标
    CPU_USAGE=$(get_cpu_usage)
    MEM_USAGE=$(get_mem_usage)
    SYSTEM_LOAD=$(get_system_load)
    
    # 报告头部
    echo -e "==== Linux系统健康报告 ====\n" >> $TEMP_FILE
    echo -e "生成时间: $(date '+%Y-%m-%d %H:%M:%S')" >> $TEMP_FILE
    echo -e "主机名: $HOSTNAME" >> $TEMP_FILE
    echo -e "IP地址: $IP_ADDRESS" >> $TEMP_FILE
    echo -e "内核版本: $KERNEL" >> $TEMP_FILE
    echo -e "运行时间: $UPTIME\n" >> $TEMP_FILE
    echo -e "当前CPU使用率: ${CPU_USAGE}%" >> $TEMP_FILE
    echo -e "当前内存使用率: ${MEM_USAGE}%" >> $TEMP_FILE
    echo -e "当前系统负载: ${SYSTEM_LOAD}\n" >> $TEMP_FILE
    
    # CPU使用率
    echo -e "\n==== CPU使用率 ====\n" >> $TEMP_FILE
    echo -e "当前CPU使用率: ${CPU_USAGE}% (阈值: ${CPU_THRESHOLD}%)" >> $TEMP_FILE
    if [[ $CPU_USAGE -ge $CPU_THRESHOLD ]]; then
        echo -e "警告: CPU使用率过高!" >> $TEMP_FILE
        alert_level="警告"
        alert_color="red"
        alert_items="${alert_items}CPU使用率(${CPU_USAGE}%)超过阈值(${CPU_THRESHOLD}%)\n"
    fi
    
    # 内存使用率
    echo -e "\n==== 内存使用率 ====\n" >> $TEMP_FILE
    echo -e "当前内存使用率: ${MEM_USAGE}% (阈值: ${MEM_THRESHOLD}%)" >> $TEMP_FILE
    if [[ $MEM_USAGE -ge $MEM_THRESHOLD ]]; then
        echo -e "警告: 内存使用率过高!" >> $TEMP_FILE
        alert_level="警告"
        alert_color="red"
        alert_items="${alert_items}内存使用率(${MEM_USAGE}%)超过阈值(${MEM_THRESHOLD}%)\n"
    fi
    
    # 系统负载
    echo -e "\n==== 系统负载 ====\n" >> $TEMP_FILE
    echo -e "当前系统负载: ${SYSTEM_LOAD} (阈值: ${LOAD_THRESHOLD})" >> $TEMP_FILE
    if (( $(echo "$SYSTEM_LOAD > $LOAD_THRESHOLD" | bc -l) )); then
        echo -e "警告: 系统负载过高!" >> $TEMP_FILE
        alert_level="警告"
        alert_color="red"
        alert_items="${alert_items}系统负载(${SYSTEM_LOAD})超过阈值(${LOAD_THRESHOLD})\n"
    fi
    
    # 磁盘使用率
    echo -e "\n==== 磁盘使用率 ====\n" >> $TEMP_FILE
    echo -e "分区使用情况:" >> $TEMP_FILE
    
    while read -r line; do
        if [[ ! -z $line ]]; then
            # 确保数字格式正确
            usage=$(echo $line | awk '{print $1}' | tr -d '[:alpha:]')
            mount=$(echo $line | awk '{print $2}')
            
            # 确保usage是数字
            if [[ "$usage" =~ ^[0-9]+$ ]]; then
                echo -e "$mount: ${usage}% (阈值: ${DISK_THRESHOLD}%)" >> $TEMP_FILE
                
                if [[ $usage -ge $DISK_THRESHOLD ]]; then
                    echo -e "警告: $mount 分区使用率过高!" >> $TEMP_FILE
                    alert_level="警告"
                    alert_color="red"
                    alert_items="${alert_items}磁盘分区 ${mount} 使用率(${usage}%)超过阈值(${DISK_THRESHOLD}%)\n"
                fi
            else
                echo -e "$mount: 无法获取使用率" >> $TEMP_FILE
            fi
        fi
    done <<< "$(get_disk_usage)"
    
    # 添加其他系统信息
    check_processes
    check_services
    check_network
    check_logins
    
    # 返回告警级别和告警项目
    echo "$alert_level|$alert_color|$alert_items"
}

# 发送邮件告警
send_email_alert() {
    local subject="$1"
    local body="$2"
    
    echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] 发送邮件告警...${NC}"
    
    # 使用mailx发送邮件，添加-S参数忽略证书验证错误
    if cat $TEMP_FILE | mailx -S smtp-auth=none -s "$subject" $EMAIL_TO 2>/dev/null; then
        echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] 邮件发送成功${NC}"
        return 0
    else
        # 如果失败，尝试使用echo直接发送简单内容
        if echo "系统健康报告，请查看附件" | mailx -S smtp-auth=none -s "$subject" $EMAIL_TO 2>/dev/null; then
            echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] 邮件发送成功（简化内容）${NC}"
            return 0
        else
            echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] 邮件发送失败，但脚本继续执行${NC}"
            # 忽略错误继续执行
            return 0
        fi
    fi
}

# 单次检查系统状态
check_once() {
    echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] 开始系统健康检查...${NC}" | tee -a $LOG_FILE
    
    # 生成健康报告并获取告警级别
    echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] 生成系统健康报告...${NC}" | tee -a $LOG_FILE
    REPORT_INFO=$(generate_health_report)
    
    # 解析报告信息
    ALERT_LEVEL=$(echo $REPORT_INFO | cut -d'|' -f1)
    ALERT_COLOR=$(echo $REPORT_INFO | cut -d'|' -f2)
    ALERT_ITEMS=$(echo $REPORT_INFO | cut -d'|' -f3)
    
    # 根据告警级别设置颜色
    if [[ $ALERT_COLOR == "red" ]]; then
        COLOR=$RED
    elif [[ $ALERT_COLOR == "yellow" ]]; then
        COLOR=$YELLOW
    else
        COLOR=$GREEN
    fi
    
    # 显示告警级别
    echo -e "${COLOR}[$(date '+%Y-%m-%d %H:%M:%S')] 系统状态: $ALERT_LEVEL${NC}" | tee -a $LOG_FILE
    
    # 只在系统状态异常时发送告警邮件
    if [[ $ALERT_LEVEL != "正常" ]]; then
        echo -e "${COLOR}告警项目:${NC}" | tee -a $LOG_FILE
        echo -e $ALERT_ITEMS | tee -a $LOG_FILE
        
        # 发送告警邮件
        SUBJECT="[系统告警] $HOSTNAME 系统健康检查 - $ALERT_LEVEL"
        send_email_alert "$SUBJECT" "$TEMP_FILE"
        
        # 记录告警到日志
        echo -e "${COLOR}[$(date '+%Y-%m-%d %H:%M:%S')] 已发送告警邮件${NC}" | tee -a $LOG_FILE
    else
        # 系统正常时不发送邮件，只记录日志
        echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] 系统运行正常，无需发送邮件${NC}" | tee -a $LOG_FILE
    fi
    
    echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] 系统健康检查完成${NC}" | tee -a $LOG_FILE
}

# 发送启动确认邮件
send_startup_email() {
    echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] 发送启动确认邮件...${NC}" | tee -a $LOG_FILE
    
    HOSTNAME=$(hostname)
    IP_ADDRESS=$(hostname -I | awk '{print $1}')
    SUBJECT="[系统监控] $HOSTNAME 系统监控服务已启动"
    
    cat > $TEMP_FILE << EOF
==== 系统监控服务启动通知 ====

主机名: $HOSTNAME
IP地址: $IP_ADDRESS
启动时间: $(date '+%Y-%m-%d %H:%M:%S')

监控配置:
- CPU使用率阈值: ${CPU_THRESHOLD}%
- 内存使用率阈值: ${MEM_THRESHOLD}%
- 磁盘使用率阈值: ${DISK_THRESHOLD}%
- 系统负载阈值: ${LOAD_THRESHOLD}
- 检测间隔: 60秒

系统监控服务已成功启动，将每分钟检测一次系统状态。
当系统指标超过设定阈值时，将自动发送告警邮件。

此邮件确认监控服务正常运行。
EOF
    
    if cat $TEMP_FILE | mailx -S smtp-auth=none -s "$SUBJECT" $EMAIL_TO 2>/dev/null; then
        echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] 启动确认邮件发送成功${NC}" | tee -a $LOG_FILE
    else
        echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] 启动确认邮件发送失败${NC}" | tee -a $LOG_FILE
    fi
}

# 主函数
main() {
    # 检查依赖
    check_dependencies
    
    # 配置邮件
    configure_mail
    
    # 发送启动确认邮件
    send_startup_email
    
    # 循环检测系统状态，每隔1分钟检测一次
    echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] 启动系统监控，每隔1分钟检测一次系统状态${NC}" | tee -a $LOG_FILE
    
    while true; do
        check_once
        echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] 等待60秒后进行下一次检测...${NC}" | tee -a $LOG_FILE
        sleep 60
    done
}

# 执行主函数
main

exit 0